package cn.xie.blog.serverutil.util;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.xie.blog.commonutil.util.IPUtil;
import jakarta.annotation.Resource;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author: xiaoxie
 * create: 2025-04-22 16:37
 * @BelongsProject: blog-parent
 * @BelongsPackage: cn.xie.blog.serverutil.util
 * description: redis工具类
 */

@Component
@ConditionalOnClass(RedisTemplate.class)
public class RedisUtil {
    Logger log = LoggerFactory.getLogger(RedisUtil.class);
    @Resource
    private RedisTemplate<String, Object> redisTemplate; // 使用泛型模板

    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            log.error(e.getMessage());
            return false;
        }
    }

    /**
     * 上锁
     * 底层是redis的setNx方法
     *
     * @param key     键
     * @param value   值
     * @param timeout 超市时间，单位秒
     * @return 上锁是否成功
     */
    public boolean tryLock(String key, Object value, long timeout) {
        boolean isSuccess = redisTemplate.opsForValue().setIfAbsent(key, value);
        if (isSuccess) {
            redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
        return isSuccess;
    }

    /**
     * 释放锁
     * @param key 键
     */
    public void unLock(String key) {
        String value = (String) redisTemplate.opsForValue().get(key);
        if (ObjectUtil.isNotNull(value)
                && value.equals(IPUtil.getIp())) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 根据key返回过期时间
     *
     * @param key 键，不能为null
     * @return 时间（秒） 返回0代表永久有效
     */
    public long getExpire(String key) {
        return Optional.ofNullable(redisTemplate.getExpire(key, TimeUnit.SECONDS)).orElse(0L);
    }

    /**
     * 删除缓存
     *
     * @param key 键
     * @return key是否存在
     */
    public boolean hasKey(String key) {
        try {
            return Optional.ofNullable(redisTemplate.hasKey(key)).orElse(false);
        } catch (Exception e) {
            log.error(e.getMessage());
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param key 要删除的键
     */
    public boolean del(String... key) {
        if (ObjectUtil.isNotEmpty(key)
                && key.length > 0) {
            if (key.length == 1) {
               return Boolean.TRUE.equals(redisTemplate.delete(key[0]));
            } else {
                Long delete = redisTemplate.delete(Arrays.asList(key));
                return delete > 0;
            }
        }
        return false;
    }

    /**
     * 放入缓存
     *
     * @param key   key
     * @param value value
     */
    public void put(String key, Object value) {
        if (StrUtil.isNotBlank(key) && ObjectUtil.isNotNull(value)) {
            redisTemplate.opsForValue().set(key, value);
        }
    }

    /**
     * 放入缓存
     *
     * @param key     key
     * @param value   value
     * @param timeout 过期时间，单位s,如果time小于等于0，将设置无限期
     */
    public void put(String key, Object value, long timeout) {
        if (StrUtil.isNotBlank(key) && ObjectUtil.isNotNull(value)) {
            if (timeout > 0) {
                redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
            } else {
                redisTemplate.opsForValue().set(key, value);
            }
        }
    }

    /**
     * 获取缓存，不刷新过期时间
     *
     * @param key key
     * @return value
     */
    public Object get(String key) {
        return StrUtil.isBlank(key) ? null :
                redisTemplate.opsForValue().get(key);
    }

    /**
     * 获取指定类型值，不存在则返回null
     *
     * @param key   key
     * @param clazz 类型
     * @return value
     */
    public <T> T getObject(String key, Class<T> clazz) {
        Object value = get(key);
        T ret = null;
        if (ObjectUtil.isNotNull(value) && clazz.isInstance(value)) {
            ret = (T) value;
        }
        return ret;
    }

    /**
     * 获取指定类型值，不存在则返回默认值
     *
     * @param key          key
     * @param defaultValue defaultValue
     * @param clazz        类型
     * @return value
     */
    public <T> T getObjectWithDefault(String key, T defaultValue, Class<T> clazz) {
        Object value = get(key);
        T ret = defaultValue;
        if (ObjectUtil.isNotNull(value) && clazz.isInstance(value)) {
            ret = (T) value;
        }
        return ret;
    }

    /**
     * 获取list值，不存在则返回默认值
     *
     * @param key          key
     * @param defaultValue 默认值
     * @return value
     */
    public <T> List<T> getListWithDefault(String key, List<T> defaultValue) {
        Object value = get(key);
        List<T> ret = defaultValue;
        if (ObjectUtil.isNotNull(value) && value instanceof List) {
            ret = (List<T>) value;
        }
        return ret;
    }

    /**
     * @param pattern 模式匹配串
     * @return 所有key的集合
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    public void deleteKeysByPrefix(String prefix) {
        Set<String> keysToDelete = new HashSet<>();
        ScanOptions options = ScanOptions.scanOptions().match(prefix + "*").count(100).build();

        redisTemplate.executeWithStickyConnection((RedisCallback<Cursor<byte[]>>) connection -> {
            try (Cursor<byte[]> cursor1 = connection.scan(options)) {
                while (cursor1.hasNext()) {
                    keysToDelete.add(new String(cursor1.next(), StandardCharsets.UTF_8));
                }
            }catch (Exception e) {
                log.error(e.getMessage());
            }
            return null;
        });

        redisTemplate.delete(keysToDelete);
    }


    // -------------------hash相关操作-------------------------

    /**
     * 获取存储在哈希表中指定字段的值
     *
     * @param key
     * @param hashKey
     * @return
     */
    public <T> T hGet(String key, String hashKey) {
        if (StrUtil.isBlank(hashKey)){
            return null;
        }
        Object value = getHashOperations().get(key, hashKey);
        if (ObjectUtil.isNotNull(value)){
            return (T) value;
        }
        return null;
    }

    /**
     * 获取 HashOperations 实例
     * @return HashOperations 对象
     */
    public HashOperations<String, Object, Object> getHashOperations() {
        return redisTemplate.opsForHash();
    }

    //  ------------------  zset -----------------------------------------------
    /**
     * 向有序集合中添加元素，或更新已有元素的分数
     *
     * @param key    缓存的key
     * @param value  添加的值
     * @param score  分数
     * @return 添加是否成功
     */
    public boolean zAdd(String key, Object value, double score) {
        try {
            redisTemplate.opsForZSet().add(key, value, score);
            return true;
        } catch (Exception e) {
            log.error("zAdd error: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 向有序集合中添加多个元素，或更新已有元素的分数
     *
     * @param key     缓存的key
     * @param values  添加的值和分数的映射
     * @return 添加的元素个数
     */
    public Long zAddAll(String key, Map<Object, Double> values) {
        try {
            return redisTemplate.opsForZSet().add((String) key, (Set<ZSetOperations.TypedTuple<Object>>) values);
        } catch (Exception e) {
            log.error("zAddAll error: {}", e.getMessage());
            return 0L;
        }
    }

    /**
     * 获取有序集合中指定范围内的元素，按分数从低到高排序（区间左闭右闭）
     *
     * @param key     缓存的key
     * @param start   开始索引（包含）
     * @param end     结束索引（包含）
     * @return 指定范围内的元素集合
     */
    public Set<Object> zRange(String key, long start, long end) {
        try {
            return redisTemplate.opsForZSet().range(key, start, end);
        } catch (Exception e) {
            log.error("zRange error: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取有序集合中指定分数范围内的元素（区间左闭右闭）
     *
     * @param key     缓存的key
     * @param min     最低分数（包含）
     * @param max     最高分数（包含）
     * @return 指定分数范围内的元素集合
     */
    public Set<Object> zRangeByScore(String key, double min, double max) {
        try {
            return redisTemplate.opsForZSet().rangeByScore(key, min, max);
        } catch (Exception e) {
            log.error("zRangeByScore error: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取有序集合中指定元素的排名（按分数从低到高排序），从1开始计数
     *
     * @param key     缓存的key
     * @param value   要查询排名的元素值
     * @return 元素的排名，不存在则返回null
     */
    public Long zRank(String key, Object value) {
        try {
            return redisTemplate.opsForZSet().rank(key, value);
        } catch (Exception e) {
            log.error("zRank error: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取有序集合中指定元素的分数
     *
     * @param key     缓存的key
     * @param value   要查询分数的元素值
     * @return 元素的分数，不存在则返回null
     */
    public Double zScore(String key, Object value) {
        try {
            return redisTemplate.opsForZSet().score(key, value);
        } catch (Exception e) {
            log.error("zScore error: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取有序集合中元素的数量
     *
     * @param key     缓存的key
     * @return 元素的数量
     */
    public Long zCardinality(String key) {
        try {
            return redisTemplate.opsForZSet().zCard(key);
        } catch (Exception e) {
            log.error("zCardinality error: {}", e.getMessage());
            return 0L;
        }
    }

    /**
     * 删除有序集合中的一个或多个元素
     *
     * @param key     缓存的key
     * @param values  要删除的元素值
     * @return 删除的元素个数
     */
    public Long zRemove(String key, Object... values) {
        try {
            return redisTemplate.opsForZSet().remove(key, values);
        } catch (Exception e) {
            log.error("zRemove error: {}", e.getMessage());
            return 0L;
        }
    }

    /**
     * 获取有序集合中指定元素的排名（按分数从高到低排序），从1开始计数
     *
     * @param key     缓存的key
     * @param value   要查询排名的元素值
     * @return 元素的排名，不存在则返回null
     */
    public Long zReverseRank(String key, Object value) {
        try {
            return redisTemplate.opsForZSet().reverseRank(key, value);
        } catch (Exception e) {
            log.error("zReverseRank error: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取有序集合中指定范围内的元素，按分数从高到低排序（区间左闭右闭）
     *
     * @param key     缓存的key
     * @param start   开始索引（包含）
     * @param end     结束索引（包含）
     * @return 指定范围内的元素集合
     */
    public Set<Object> zReverseRange(String key, long start, long end) {
        try {
            return redisTemplate.opsForZSet().reverseRange(key, start, end);
        } catch (Exception e) {
            log.error("zReverseRange error: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取有序集合中指定分数范围内的元素，按分数从高到低排序（区间左闭右闭）
     *
     * @param key     缓存的key
     * @param min     最低分数（包含）
     * @param max     最高分数（包含）
     * @return 指定分数范围内的元素集合
     */
    public Set<Object> zReverseRangeByScore(String key, double min, double max) {
        try {
            return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
        } catch (Exception e) {
            log.error("zReverseRangeByScore error: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 为有序集合中指定元素的分数增加一个增量
     *
     * @param key     缓存的key
     * @param value   要增加分数的元素值
     * @param delta   分数的增量（可以是负数）
     * @return 增量操作后的分数
     */
    public Double zIncrBy(String key, Object value, double delta) {
        try {
            return redisTemplate.opsForZSet().incrementScore(key, value, delta);
        } catch (Exception e) {
            log.error("zIncrBy error: {}", e.getMessage());
            return null;
        }
    }
}
